﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Timers;
using System.Threading.Tasks;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using Bymed.SpineScan.Service;
using Bymed.SpineScan.DataModel;
using Bymed.SpineScan.DataModel.TestItem;
using Bymed.SpineScan.WPF.Model;
using Bymed.SpineScan.WPF.Shared;
using Bymed.SpineScan.WPF.ViewModel.TestItemViewModel;
using Bymed.SpineScan.Service.Shared;

namespace Bymed.SpineScan.WPF.ViewModel
{
    public class StartTestViewModel : AllTestItemsAndPatientInfoViewModel
    {
        public const string GoToNextPageMessageToken = "StartTestGoToNextPageMessage";
        public const string MoveToNextTestItemMessageToken = "StartTestMoveToNextTestItemMessage";
        protected SingleTestItemViewModel CurrentTestItemViewModel;
        private int _currentTestItemIndex;

        /// <summary>
        /// Initializes a new instance of the StartTestViewModel class.
        /// </summary>
        public StartTestViewModel(IDataService dataService)
            : base(dataService) { }

        public override void ResetProperties()
        {
            base.ResetProperties();

            foreach (var item in TestItemList)
            {
                ViewModelHelper.ResetSingleTestItemTestResult(item);
            }

            // Set the first item as ready to go.
            _currentTestItemIndex = 0;
            SetTestItemReadyToTest(_currentTestItemIndex);
        }

        protected override void RegisterMessages()
        {
            base.RegisterMessages();
            RegisterSendReceivedTestMessageMessage();
        }

        /// <summary>
        /// The <see cref="CurrentTestItemName" /> property's name.
        /// </summary>
        public const string CurrentTestItemNamePropertyName = "CurrentTestItemName";

        private string _currentTestItemName = string.Empty;

        /// <summary>
        /// Sets and gets the CurrentTestItemName property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string CurrentTestItemName
        {
            get
            {
                return _currentTestItemName;
            }

            set
            {
                if (_currentTestItemName == value)
                {
                    return;
                }

                RaisePropertyChanging(CurrentTestItemNamePropertyName);
                _currentTestItemName = value;
                RaisePropertyChanged(CurrentTestItemNamePropertyName);
            }
        }

        private RelayCommand<SingleTestItemViewModel> _selectTestItemCommand;

        /// <summary>
        /// Gets the SelectTestItemCommand.
        /// </summary>
        public RelayCommand<SingleTestItemViewModel> SelectTestItemCommand
        {
            get
            {
                return _selectTestItemCommand
                    ?? (_selectTestItemCommand = new RelayCommand<SingleTestItemViewModel>(ExecuteSelectTestItemCommand));
            }
        }

        private void ExecuteSelectTestItemCommand(SingleTestItemViewModel parameter)
        {
            try
            {
                if (parameter != null && parameter != CurrentTestItemViewModel)
                {
                    if (CurrentTestItemViewModel != null)
                    {
                        ViewModelHelper.ConvertTestResultBasedOnTestType(CurrentTestItemViewModel);
                        CurrentTestItemViewModel.SetFormattedTestResult();
                    }

                    _currentTestItemIndex = TestItemList.IndexOf(parameter);
                    CurrentTestItemViewModel = parameter;
                    CurrentTestItemName = parameter.TestItemName;
                }
            }
            catch (Exception ex)
            {
                RequestDisplayErrorMessage(ex.Message);
            }
        }

        private RelayCommand _nextCommand;

        /// <summary>
        /// Gets the NextCommand.
        /// </summary>
        public RelayCommand NextCommand
        {
            get
            {
                return _nextCommand ?? (_nextCommand = new RelayCommand(
                    ExecuteNextCommand,
                    CanExecuteNextCommand));
            }
        }

        private void ExecuteNextCommand()
        {
            try
            {
                (new DeviceConfigurationService()).DecreaseAvailableAuthorisedUsageCount();

                ViewModelHelper.SyncTestItemListToProperties(this);

                // Send GoToNextPageMessage to StartTestPage
                Messenger.Default.Send<StartTestViewModel>(this, GoToNextPageMessageToken);
            }
            catch (Exception ex)
            {
                RequestDisplayErrorMessage(ex.Message);
            }
        }

        private bool CanExecuteNextCommand()
        {
            return CheckAllTestsFinished();
        }

        /// <summary>
        /// Register the SendReceivedTestMessageMessage from MainViewModel
        /// </summary>
        private void RegisterSendReceivedTestMessageMessage()
        {
            Messenger.Default.Register<string>(this, MainViewModel.SendReceivedTestMessageMessageToken, p =>
            {
                try
                {
                    ShowTestResult(p);
                }
                catch (Exception ex)
                {
                    RequestDisplayErrorMessage(ex.Message);
                }
            });
        }

        /// <summary>
        /// Check if all tests finished.
        /// </summary>
        /// <returns>True means finished. False means NOT finished yet.</returns>
        private bool CheckAllTestsFinished()
        {
            var isTestFinished = true;

            foreach (var testItem in TestItemList)
            {
                if (!testItem.CheckIfTestFinished())
                {
                    isTestFinished = false;
                    break;
                }
            }

            return isTestFinished;
        }

        /// <summary>
        /// Get the TestItem by the specified index in selected TestItem list.
        /// </summary>
        /// <param name="itemIndex">The index in selected TestItem list.</param>
        /// <returns>The matched TestItem.</returns>
        private SingleTestItemViewModel GetTestItemByIndex(int itemIndex)
        {
            if (itemIndex < 0 || itemIndex > TestItemList.Count - 1)
            {
                return null;
            }
            else
            {
                return TestItemList[itemIndex];
            }
        }

        /// <summary>
        /// Set the TestItem as ready to go by the index in the selected TestItem list.
        /// </summary>
        /// <param name="itemIndex">The index in the selected TestItem list.</param>
        private void SetTestItemReadyToTest(int itemIndex)
        {
            if (CurrentTestItemViewModel != null)
            {
                CurrentTestItemViewModel.IsReadyToTest = false;
            }

            CurrentTestItemViewModel = TestItemList[itemIndex];
            CurrentTestItemViewModel.IsReadyToTest = true;
            CurrentTestItemName = CurrentTestItemViewModel.TestItemName;
        }

        /// <summary>
        /// Move current TestItem to the next one.
        /// </summary>
        private void MoveToNextTestItem()
        {
            // If current item is not the last one.
            if (_currentTestItemIndex < TestItemList.Count - 1)
            {
                _currentTestItemIndex += 1;
                SetTestItemReadyToTest(_currentTestItemIndex);
            }
            else
            {
                // Set the first TestItem as ready to go.
                _currentTestItemIndex = 0;
                SetTestItemReadyToTest(_currentTestItemIndex);
            }

            // Send MoveToNextTestItemMessage to StartTestPage.
            Messenger.Default.Send<SingleTestItemViewModel>(CurrentTestItemViewModel, MoveToNextTestItemMessageToken);
        }

        /// <summary>
        /// Show test result based on the test message from device.
        /// </summary>
        /// <param name="testMessage">The test message from device</param>
        private void ShowTestResult(string testMessage)
        {
            var testItem = TestItemFactory.GetTestItem(testMessage);

            if (testItem != null)
            {
                if (!IsCurrentTestTypeMatched(testItem))
                {
                    RequestDisplayMessage("所选测试项目类型不匹配");
                }
                else
                {
                    RequestClearMessage();

                    if (CurrentTestItemViewModel is TestItemScoliosisATIViewModel)
                    {
                        SetupTestItemScoliosisATIViewModelProperties(testItem, (TestItemScoliosisATIViewModel)CurrentTestItemViewModel);
                    }

                    CurrentTestItemViewModel.FirstTestResult = testItem.FirstResult;
                    CurrentTestItemViewModel.SecondTestResult = testItem.SecondResult;
                    CurrentTestItemViewModel.ResultRawData = testItem.ResultRawData;
                    CurrentTestItemViewModel.SetFormattedTestResult();

                    if (CurrentTestItemViewModel.CheckIfTestFinished())
                    {
                        if (CheckAllTestsFinished())
                        {
                            RequestShowMessageBox("所有测试已完成");
                        }
                        else
                        {
                            MoveToNextTestItem();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Setup the properties of GraphData & TestPositionName for TestItemScoliosisATIViewModel
        /// </summary>
        /// <param name="testItem">The instance of ScoliosisATITest.</param>
        /// <param name="testItemScoliosisATI">The instance of TestItemScoliosisATIViewModel.</param>
        private void SetupTestItemScoliosisATIViewModelProperties(TestItemBase testItem, TestItemScoliosisATIViewModel testItemScoliosisATI)
        {
            if (testItem.Graph.Count == 100)
            {
                testItemScoliosisATI.GerenateGraphData(testItem.Graph);
            }
            else if (testItemScoliosisATI.GraphData.Count > 0)
            {
                testItemScoliosisATI.GraphData.Clear();
            }

            testItemScoliosisATI.SetupTestPositionName(testItem.TestPosition);
        }

        /// <summary>
        /// If CurrentTestItemViewModel is matched testItem type.
        /// </summary>
        /// <param name="testItem">Current test item.</param>
        /// <returns>True means matched. False means NOT.</returns>
        private bool IsCurrentTestTypeMatched(TestItemBase testItem)
        {
            if (CurrentTestItemViewModel.TestItemName.EndsWith(testItem.TestItemName) ||
                IsCustomizedTestMatched(testItem))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// If CurrentTestItemViewModel is matched CustomizedTest.
        /// </summary>
        /// <param name="testItem">The TestItem eceived from hardware</param>
        /// <returns>True means CurrentTestItemViewModel is matched CustomizedTest. False means NOT.</returns>
        private bool IsCustomizedTestMatched(TestItemBase testItem)
        {
            if (GeneralUtility.IsPositiveInteger(CurrentTestItemViewModel.TestItemName) &&
                CurrentTestItemViewModel.TestItemType.EndsWith(testItem.TestItemName))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
